#include "shadow2d.h"
#include "qgl.h"
#include <stdlib.h>
#include "math.h"
#include <vector>
#include "artistic.h"
using namespace std;
//was 280
#define GRID_SIZE 560
#define qMAX 4
#define a .6
#define b 2
#define C 1.5
Shadow2D::Shadow2D()
{
    m_grid = new float[1];
    m_maxw = CANVAS_SIZE;
    m_maxh = CANVAS_SIZE;

}

Shadow2D::~Shadow2D()
{
    delete[] m_grid;
}

void Shadow2D::populateGrid(Bud *bud)
{
    delete[] m_grid;
   m_grid = new float[GRID_SIZE * GRID_SIZE];
   for(int i = 0; i< GRID_SIZE * GRID_SIZE; i++){
       m_grid[i] = 0.0f;
   }

   this->recurPopulate(bud);

}

void Shadow2D::recurPopulate(Bud *bud){
    int startw, starth;
    int ind = 0;
    if(!bud->aux){
        convertToIndex(bud->end,startw, starth);
        for(int q =0 ; q<qMAX; q++){
            for(int p = -q; p<=q; p++){
                if( (starth + q >= 0) && (starth + q )< GRID_SIZE && (startw + p >= 0) && (startw +p < GRID_SIZE)){
                    ind = startw + p + (starth + q) * GRID_SIZE;
                    m_grid[ind] += a* pow((double) b,(double) -q);
                }
            }
        }
    }
    else
        recurPopulate(bud->aux);

    if(!bud->term){
        convertToIndex(bud->end,startw, starth);
        for(int q =0 ; q<qMAX; q++){
            for(int p = -q; p<=q; p++){
                if((starth + q >= 0) && (starth + q )< GRID_SIZE && (startw + p > 0) && (startw +p < GRID_SIZE)){
                    ind = startw + p + (starth + q) * GRID_SIZE;
                    m_grid[ind] += a* pow((double) b,(double) -q);
                }
            }
        }
    }
    else
        recurPopulate(bud->term);
}

inline void Shadow2D::convertToIndex(float3 loc, int &w, int &h){
    w = (int) ((loc.x / m_maxw) * GRID_SIZE + .5);
    h =  (int) (GRID_SIZE - ((loc.y / m_maxh) * GRID_SIZE) + .5) ;
}

inline void Shadow2D::indexToXY(int w, int h, float3 &loc){
    loc.x = ((float) w)/ ((float) GRID_SIZE) * m_maxw;
    loc.y = ((float) GRID_SIZE - (float) h)/ ((float) GRID_SIZE) * m_maxw;
}

void Shadow2D::queryQ(Bud *bud){
    int w = 0,h = 0;
    convertToIndex(bud->end, w, h);
    int ind = w + GRID_SIZE*h;
    vector<float3> bests;
    if(!bud->aux){
        if(w < 0 || h< 0|| h >= GRID_SIZE || w >= GRID_SIZE){
            bud->aQ = 0;
            bud->aV = float3(0,1,0);
        }
        else{
            bud->aQ = max(C - m_grid[ind] + a, 0.0);


            float min = 100.0;
            for(int i = -1; i<2; i++){
                for(int j = -1; j<2; j++){
                    int tindx = (w + i) + (h+j)*GRID_SIZE;
                    if(w+i > 0 && w + i <GRID_SIZE && h+j > 0 && h+j < GRID_SIZE ){
                        if(m_grid[tindx]< min && (i!=0 || j!=0)){
                            min = m_grid[tindx];
                            float3 best = float3(w+i, h+j, 0);
                            bests.clear();
                            bests.push_back(best);
                        }
                        if(m_grid[tindx]== min && (i!=0 || j!=0) ){
                            float3 best = float3(w+i, h+j, 0);
                            bests.push_back(best);
                        }
                    }
                    else{
                        bud->aQ = 0;
                    }
                }
            }

            float3 dir;
            if(bests.size() != 0){
                int decider = (rand() %bests.size());
                dir = float3(bests[decider].x - w,h- bests[decider].y , 0);
                dir.normalize();
            }
            else{
                dir = float3(0,1,0);
                bud->aQ = 0;
            }

            bud->aV = dir;
        }
    }
    else
        queryQ(bud->aux);

    bests.clear();
    if(!bud->term){
        if(w < 0 || h< 0|| h >= GRID_SIZE || w >= GRID_SIZE){
            bud->tQ = 0;
            bud->tV = float3(0,1,0);
        }
        else{
            if(!bud->aux){
                bud->tQ = max(C - m_grid[ind] + 2.0*a, 0.0);
                bud->aQ = 0;
            }
            else{
                bud->tQ = max(C - m_grid[ind] + a, 0.0);
            }
            float min = 100.0;
            for(int i = -1; i<2; i++){
                for(int j = -1; j<2; j++){
                    int tindx = (w + i) + (h+j)*GRID_SIZE;
                    if(w+i >= 0 && w + i < GRID_SIZE && h+j >= 0 && h+j < GRID_SIZE ){
                        if(m_grid[tindx]< min && (i!=0 || j!=0)){
                            min = m_grid[tindx];
                            float3 best = float3(w+i, h+j, 0);
                            bests.clear();
                            bests.push_back(best);
                        }
                        if(m_grid[tindx]== min && (i!=0 || j!= 0)){
                            float3 best = float3(w+i, h+j, 0);
                            bests.push_back(best);
                        }
                    }
                }
            }
            float3 dir;
            if(bests.size() != 0){
                int decider = (rand() %bests.size());
                dir = float3(bests[decider].x - w, h - bests[decider].y, 0);
                dir.normalize();
            }
            else{
                dir = float3(0,1,0);
                bud->tQ = 0;
            }
            bud->tV = dir;
        }
    }
    else
        queryQ(bud->term);
}



void Shadow2D::draw(){
    float size = m_maxw / (float) GRID_SIZE;
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    float3 loc;
    for(int i =0; i<GRID_SIZE; i++){
        for(int j =0 ; j< GRID_SIZE; j++){
            this->indexToXY(i,j+1,loc);
            glColor3f(.5, .5, .5);
            glBegin(GL_QUADS);
                glVertex2f(loc.x , loc.y);
                glVertex2f(loc.x + size, loc.y);
                glVertex2f(loc.x + size, loc.y + size);
                glVertex2f(loc.x, loc.y + size);
            glEnd();
        }
    }

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    for(int i =0; i<GRID_SIZE; i++){
        for(int j =0 ; j< GRID_SIZE; j++){
            this->indexToXY(i,j+1,loc);
            glColor4f(1.0, 0, 0, m_grid[i + GRID_SIZE*j]);
            //cout<<m_grid[i+m_w*j]<<endl;
            glBegin(GL_QUADS);
                glVertex2f(loc.x , loc.y);
                glVertex2f(loc.x + size, loc.y);
                glVertex2f(loc.x + size, loc.y + size);
                glVertex2f(loc.x, loc.y + size);
            glEnd();
        }
    }
    glDisable(GL_BLEND);
}
